/*
 * AD9954 Register function driver source file
 */

#include <stdint.h>
#include <stdbool.h>
#include "hw_ad9954.h"
#include "ad9954.h"

static uint32_t ad9954_clock = 0;

void ad9954_init(void)
{
	ad9954_io_init();
	ad9954_hwrst();
}

void ad9954_rst(void)
{
	ad9954_hwrst();
}

void ad9954_line_set(enum AD9954_Line wire, bool set)
{
    switch (wire) {
        case LINE_OSK:
            ad9954_osk_set(set);
            break;
        case LINE_PS0:
            ad9954_ps0_set(set);
            break;
        case LINE_PS1:
            ad9954_ps1_set(set);
            break;
        default:
            break;
    }
}

uint32_t ad9954_set_clk(uint32_t refclk, uint32_t mul)
{
	uint32_t tmp;
	uint8_t cache[3];
    
	refclk=refclk>25000000?25000000:refclk;
	mul=mul>0x0000001f?0x0000001f:mul;
    ad9954_clock=refclk*mul;

	if(ad9954_clock>250000000)
		tmp=(mul<<3)|VCO_RANGE;
	else
		tmp=mul<<3;

	//tmp |= (XTAL_OUT_EN | CHG_PUMP_BIT1 | CHG_PUMP_BIT0);

	cache[0]=(tmp>>16)&0xff;
	cache[1]=(tmp>>8)&0xff;
	cache[2]=tmp&0xff;

	ad9954_register_write(AD9954_CFR2, cache);
	ad9954_update();
	//ad9954_iosync();

	return ad9954_clock;
}

void ad9954_single_tone(uint32_t freq, uint32_t phase)
{
    // 1<<32 is too large, so him use 0xffffffff
	uint32_t fword=(uint32_t)((double)(0xffffffff)/(double)ad9954_clock*(double)freq);
	uint32_t pword=(uint32_t)((double)(1<<14)/(double)360*(double)phase);
	uint8_t cache[4];

	uint32_t cfr1=AUTO_CLR_PHASE_ACC | AUTO_CLR_FREQ_ACC;

	cache[0]=(cfr1>>24)&0xff;
	cache[1]=(cfr1>>16)&0xff;
	cache[2]=(cfr1>>8)&0xff;
	cache[3]=cfr1&0xff;
	ad9954_register_write(AD9954_CFR1, cache);

	cache[0]=(fword>>24)&0xff;
	cache[1]=(fword>>16)&0xff;
	cache[2]=(fword>>8)&0xff;
	cache[3]=fword&0xff;
	ad9954_register_write(AD9954_FTW0, cache);

	cache[0]=(pword>>8)&0x3f;
	cache[1]=pword&0xff;
	ad9954_register_write(AD9954_POW0, cache);

	ad9954_update();
}

/*
 *  Function: ad9954_linear_sweep
 *      The Linear sweep mode generator.
 *
 *  Input: start frequency, stop frequency, rising time (us), falling time (us),
 *         rising frequency sweep rouphness and falling frequency sweep rouphness.
 *
 *  Algorithm:
 *      FTWx = (real freq) / (clock freq) * 2^32
 *      xLSCW = (rouphness << 32) | (delta freq word)
 *          (delta freq) = (stop freq word - start freq word) / (steps need)
 *          (steps need) = (ranging time) / ( rouphness * (clock cycle) )
 *          (clock cycle) = 1 / (clock freq)
 */
void ad9954_linear_sweep(uint32_t freq0, uint32_t freq1,
                         uint32_t rising, uint32_t falling,
                         uint8_t rRouphness, uint8_t fRouphness)
{
    uint32_t ftw0 = (uint32_t)((double)(0xffffffff)/(double)ad9954_clock*(double)freq0);
    uint32_t ftw1 = (uint32_t)((double)(0xffffffff)/(double)ad9954_clock*(double)freq1);
    uint32_t plscwLow = (uint32_t)( (double)(ftw1-ftw0)*(double)rRouphness*(1000000/(double)rising)/(double)ad9954_clock );
    uint8_t  plscwHigh = rRouphness;
    uint32_t nlscwLow = (uint32_t)( (double)(ftw1-ftw0)*(double)fRouphness*(1000000/(double)falling)/(double)ad9954_clock );
    uint8_t  nlscwHigh = fRouphness;
	uint8_t cache[5];

	uint32_t cfr1 = SWEEP_EN;
    
	cache[0]=(cfr1>>24)&0xff;
	cache[1]=(cfr1>>16)&0xff;
	cache[2]=(cfr1>>8)&0xff;
	cache[3]=cfr1&0xff;
	ad9954_register_write(AD9954_CFR1, cache);

	cache[0]=(ftw0>>24)&0xff;
	cache[1]=(ftw0>>16)&0xff;
	cache[2]=(ftw0>>8)&0xff;
	cache[3]=ftw0&0xff;
	ad9954_register_write(AD9954_FTW0, cache);

	cache[0]=(ftw1>>24)&0xff;
	cache[1]=(ftw1>>16)&0xff;
	cache[2]=(ftw1>>8)&0xff;
	cache[3]=ftw1&0xff;
	ad9954_register_write(AD9954_FTW1, cache);

    cache[0]=plscwHigh;
	cache[1]=(plscwLow>>24)&0xff;
	cache[2]=(plscwLow>>16)&0xff;
	cache[3]=(plscwLow>>8)&0xff;
	cache[4]=plscwLow&0xff;
	ad9954_register_write(AD9954_PLSCW, cache);

    cache[0]=nlscwHigh;
	cache[1]=(nlscwLow>>24)&0xff;
	cache[2]=(nlscwLow>>16)&0xff;
	cache[3]=(nlscwLow>>8)&0xff;
	cache[4]=nlscwLow&0xff;
	ad9954_register_write(AD9954_NLSCW, cache);

	ad9954_update();
}

/*
 *  Function: ad9954_fsk_set
 *      A simple 2-tone FSK setting function. (using PS0 as sending port).
 *
 *  Input: freq when sending 0, freq when sending 1.
 *
 *  Algorithm:
 *      The FSK contains only two frequency, and need an external pin as a key to shifting the frequency.
 *      So I config the profile<0> and profile<1> as the two single frequency when shifting using PS0.
 *      The two profile controls only 1 ram address each to store the frequency, using Direct Switch Mode
 *      to shifting.
 */
void ad9954_fsk_set(uint32_t freq0, uint32_t freq1)
{
	uint32_t cfr1 = RAM_EN;
    uint32_t rscw0Low = makeRscwBytes(0x000, 0x000, RAM_DIRECT_SWITCH, 0);
    uint32_t rscw1Low = makeRscwBytes(0x001, 0x001, RAM_DIRECT_SWITCH, 0);
    uint32_t ftw0 = (uint32_t)((double)(0xffffffff)/(double)ad9954_clock*(double)freq0);
    uint32_t ftw1 = (uint32_t)((double)(0xffffffff)/(double)ad9954_clock*(double)freq1);
	uint8_t cache[5];
    
	cache[0]=(cfr1>>24)&0xff;
	cache[1]=(cfr1>>16)&0xff;
	cache[2]=(cfr1>>8)&0xff;
	cache[3]=cfr1&0xff;
	ad9954_register_write(AD9954_CFR1, cache);
    
    cache[0]=0xff;  // not important in fsk
	cache[1]=(rscw0Low>>24)&0xff;
	cache[2]=(rscw0Low>>16)&0xff;
	cache[3]=(rscw0Low>>8)&0xff;
	cache[4]=rscw0Low&0xff;
	ad9954_register_write(AD9954_RSCW0, cache);
    
    cache[0]=0xff;  // not important in fsk
	cache[1]=(rscw1Low>>24)&0xff;
	cache[2]=(rscw1Low>>16)&0xff;
	cache[3]=(rscw1Low>>8)&0xff;
	cache[4]=rscw1Low&0xff;
	ad9954_register_write(AD9954_RSCW1, cache);
    
    // write ram with profile 0
    ad9954_line_set(LINE_PS1, 0);
    ad9954_line_set(LINE_PS0, 0);
	ad9954_ram_write(&ftw0, 1);

    // write ram with profile 1
    ad9954_line_set(LINE_PS1, 0);
    ad9954_line_set(LINE_PS0, 1);
	ad9954_ram_write(&ftw1, 1);

	ad9954_update();
}
